package io.github.viscent.mtpattern.ch13.pipeline.example;

import io.github.viscent.util.Debug;
import io.github.viscent.util.Tools;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

/**
 * 写记录工具
 */
@SuppressWarnings("all")
public class RecordWriter {

    private static final RecordWriter INSTANCE = new RecordWriter();

    /**
     * HashMap不是线程安全的，但RecordWriter实例是在单线程中使用的，因此不会产生问题。
     * key为fileKey,value为对应PrintWriter
     */
    private static Map<String, PrintWriter> printWriterMap = new HashMap<>();

    private static String baseDir;

    /**
     * 字段分隔符
     */
    private static final char FIELD_SEPARATOR = '|';

    /**
     * SimpleDateFormat不是线程安全的，但RecordWriter实例是在单线程中使用的，因此不会产生问题。
     */
    private static final SimpleDateFormat DIRECTORY_NAME_FORMATTER = new SimpleDateFormat("yyMMdd");
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:MM:ss");

    private static final DecimalFormat FILE_INDEX_FORMATTER = new DecimalFormat("0000");

    private static final int RECORD_JOIN_SIZE = Config.RECORD_JOIN_SIZE;

    private static final FieldPosition FIELD_POS = new FieldPosition(DateFormat.Field.DAY_OF_MONTH);

    private RecordWriter() {
        baseDir = Tools.getWorkingDir("ch13");
        Debug.info("baseDir %s", baseDir);
    }

    public static RecordWriter getInstance() {
        return INSTANCE;
    }

    /**
     * 写入记录
     *
     * @param records         记录
     * @param targetFileIndex 写入文件
     * @return File
     */
    public File write(Record[] records, int targetFileIndex) throws IOException {
        if (null == records || 0 == records.length) {
            throw new IllegalArgumentException("records is null or empty");
        }
        int recordCount = records.length;
        // 记录日期
        String recordDay = DIRECTORY_NAME_FORMATTER.format(records[0].getOperationTime());
        // 用来标识文件
        String fileKey = recordDay + '-' + targetFileIndex;
        PrintWriter pwr = printWriterMap.get(fileKey);
        String pathname = baseDir + '/' + recordDay + "/subspsync-gw-" + FILE_INDEX_FORMATTER.format(targetFileIndex) + ".dat";
        if (null == pwr) {
            File file = new File(pathname);
            File dir = file.getParentFile();
            if (!dir.exists() && !dir.mkdirs()) {
                throw new IOException("No such directory:" + dir);
            }
            // PrintWriter可以将任何类型的基本数据直接写入文件中，而BufferedWriter不行(可以试下写单个数字效果)
            pwr = new PrintWriter(new BufferedWriter(new FileWriter(file, true), Config.WRITER_BUFFER_SIZE));
            printWriterMap.put(fileKey, pwr);
        }
        StringBuffer strBuf = new StringBuffer(40);
        int i = 0;
        for (Record record : records) {
            i++;
            pwr.print(record.getId());
            pwr.print(FIELD_SEPARATOR);
            pwr.print(record.getMsisdn());
            pwr.print(FIELD_SEPARATOR);
            pwr.print(record.getProductId());
            pwr.print(FIELD_SEPARATOR);

            pwr.print(record.getPackageId());
            pwr.print(FIELD_SEPARATOR);
            pwr.print(record.getOperationType());
            pwr.print(FIELD_SEPARATOR);

            strBuf.delete(0, 40);
            pwr.print(sdf.format(record.getOperationTime(), strBuf, FIELD_POS));
            pwr.print(FIELD_SEPARATOR);

            strBuf.delete(0, 40);
            pwr.print(sdf.format(record.getEffectiveDate(), strBuf, FIELD_POS));

            strBuf.delete(0, 40);
            pwr.print(FIELD_SEPARATOR);
            pwr.print(sdf.format(record.getDueDate(), strBuf, FIELD_POS));
            pwr.print('\n');

            if (0 == (i % RECORD_JOIN_SIZE)) {
                pwr.flush();
                i = 0;
                // Thread.yield();
            }
        }
        if (i > 0) {
            pwr.flush();
        }
        File file = null;
        // 处理当前文件中的最后一组记录
        if (recordCount < Config.RECORD_SAVE_CHUNK_SIZE) {
            pwr.close();
            file = new File(pathname);
            printWriterMap.remove(fileKey);
        }
        return file;
    }

    /**
     * 写完记录
     *
     * @param recordDay       记录日期
     * @param targetFileIndex 写入文件
     * @return File
     */
    public File finishRecords(String recordDay, int targetFileIndex) {
        String fileKey = recordDay + '-' + targetFileIndex;
        PrintWriter pwr = printWriterMap.get(fileKey);
        File file = null;
        if (null != pwr) {
            pwr.flush();
            pwr.close();
            String pathname = baseDir + '/' + recordDay + "/subspsync-gw-" + FILE_INDEX_FORMATTER.format(targetFileIndex) + ".dat";
            file = new File(pathname);
            printWriterMap.remove(fileKey);
        }
        return file;
    }

    /**
     * 备份已传输完毕的文件
     */
    public static void backupFile(final File file) {
        String recordDay = file.getParentFile().getName();
        File destFile = new File(baseDir + "/backup/" + recordDay);
        if (!destFile.exists()) {
            if (!destFile.mkdirs()) {
                return;
            }
        }
        destFile = new File(destFile, file.getName());
        try {
            Files.move(file.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            throw new RuntimeException("Failed to backup file " + file);
        }
    }

    /**
     * 所有文件备份完毕后，清理掉空文件夹
     */
    public static void purgeDir() {
        File[] dirs = new File(baseDir).listFiles();
        for (File dir : dirs) {
            if (dir.isDirectory() && 0 == dir.list().length) {
                dir.delete();
            }
        }
    }

}
